package com.iteaj.iot.boot;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReflectUtil;
import com.iteaj.iot.*;
import com.iteaj.iot.boot.core.IotCoreConfiguration;
import com.iteaj.iot.business.ProtocolHandleFactory;
import com.iteaj.iot.client.ClientComponent;
import com.iteaj.iot.client.component.SocketClientComponent;
import com.iteaj.iot.codec.filter.CombinedFilter;
import com.iteaj.iot.event.FrameworkEventListener;
import com.iteaj.iot.server.SocketServerComponent;
import com.iteaj.iot.websocket.WebSocketFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@AutoConfigureAfter(IotCoreConfiguration.class)
public class IotApplication implements ApplicationRunner {

    private final IotThreadManager threadManager;
    private final IotCoreProperties coreProperties;
    private final ComponentFactory componentFactory;
    private final ApplicationContext applicationContext;
    private final ProtocolTimeoutManager timeoutManager;
    private final ProtocolHandleFactory protocolHandleFactory;

    private static Logger logger = LoggerFactory.getLogger(IotApplication.class);

    public IotApplication(IotThreadManager threadManager, IotCoreProperties coreProperties, ComponentFactory componentFactory
            , ApplicationContext applicationContext, ProtocolHandleFactory protocolHandleFactory, ProtocolTimeoutManager timeoutManager) {
        this.threadManager = threadManager;
        this.coreProperties = coreProperties;
        this.timeoutManager = timeoutManager;
        this.componentFactory = componentFactory;
        this.applicationContext = applicationContext;
        this.protocolHandleFactory = protocolHandleFactory;
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        List<LifeCycle> lifeCycles = applicationContext
                .getBeanProvider(LifeCycle.class)
                .stream().filter(item ->
                        !(item instanceof IotThreadManager)
                                && !(item instanceof ComponentFactory)
                                && !(item instanceof FrameworkComponent)
                                && !(item instanceof ProtocolTimeoutManager))
                .collect(Collectors.toList());

        List<LifeCyclePostProcessor> processors = applicationContext
                .getBeanProvider(LifeCyclePostProcessor.class)
                .stream()
                .collect(Collectors.toList());

        try {
            // 初始化所有组件
            initFramework();

            // 所有组件已经初始化完成
            processors.forEach(item -> item.postProcessBeforeStart());

            // 启动所有生命周期组件
            startLifeCycle(lifeCycles);

            // 所有组件已经启动完成
            processors.forEach(item -> item.postProcessAfterStart());
        } catch (Exception e) {
            this.threadManager.close();
            this.timeoutManager.close();
            this.componentFactory.close();
            if(CollectionUtil.isNotEmpty(lifeCycles)) {
                lifeCycles.forEach(item -> {
                    try {
                        item.close();
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                });
            }

            throw e;
        }

    }

    private void startLifeCycle(List<LifeCycle> lifeCycles) {
        // 启动协议超时管理器
        timeoutManager.start(this);

        // 启动其它的生命周期组件
        if(CollectionUtil.isNotEmpty(lifeCycles)) {
            lifeCycles.forEach(item -> item.start(this));
        }

        // 启动所有组件
        componentFactory.start(this);
    }

    private void initFramework() {
        if(logger.isInfoEnabled()) {
            logger.info("初始化组件");
        }

        List<FrameworkEventListener> listeners = applicationContext
                .getBeanProvider(FrameworkEventListener.class)
                .stream().collect(Collectors.toList());

        Map<? extends Class<?>, CombinedFilter> filterMap = applicationContext
                .getBeanProvider(CombinedFilter.class)
                .stream()
                .collect(Collectors.toMap(item -> ClassUtil.getTypeArgument(item.getClass()), item -> item));

        // 构建框架各个组件管理器
        FrameworkManager.getInstance()
                .build(listeners, this.threadManager, this.componentFactory
                        , this.protocolHandleFactory, this.timeoutManager);

        // 注册组件
        applicationContext.getBeanProvider(FrameworkComponent.class).forEach(item -> {
            componentFactory.register(item);
            if(item instanceof ProtocolFactory) {
                this.timeoutManager.register(((ProtocolFactory<?>) item).getDelegation());
            }

            if(item.getFilter() == CombinedFilter.DEFAULT
                    || item.getFilter() == WebSocketFilter.DEFAULT) {
                CombinedFilter filter = filterMap.get(item.getClass());
                if(filter != null) {
                    if(item instanceof SocketServerComponent) {
                        ((SocketServerComponent<?, ?>) item).setFilter(filter);
                    } else if(item instanceof SocketClientComponent) {
                        ((SocketClientComponent<?, ?>) item).setFilter(filter);
                    }
                }
            }
        });

        // 注册协议处理器
        applicationContext.getBeanProvider(ProtocolHandle.class).forEach(item -> {
            protocolHandleFactory.register(item);
        });
    }

    @Bean
    @Order
    public LoggerLifeCyclePostProcessor loggerLifeCyclePostProcessor() {
        return new LoggerLifeCyclePostProcessor();
    }
}
